package cn.ywang.spring.orm.framework;

import cn.ywang.spring.orm.framework.QueryRule.Rule;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author BeatificWang
 * @date 2019/5/4 23:41
 */
public class QueryRuleSqlBuilder {

	private int CURR_INDEX = 0; //记录参数所在的位置
	private List<String> properties; //保存列名列表
	private List<Object> values; //保存参数值列表
	private List<Order> orders; //保存排序规则列表

	private String whereSql = "";
	private String orderSql = "";
	private Object[] valueArr = new Object[]{};
	private Map<Object, Object> valueMap = new HashMap<Object, Object>();

	/**
	 * 或得查询条件
	 *
	 * @return
	 */
	public String getWhereSql() {
		return this.whereSql;
	}

	/**
	 * 获得排序条件
	 *
	 * @return
	 */
	public String getOrderSql() {
		return this.orderSql;
	}

	/**
	 * 获得参数值列表
	 *
	 * @return
	 */
	public Object[] getValues() {
		return this.valueArr;
	}

	/**
	 * 获取参数列表
	 *
	 * @return
	 */
	public Map<Object, Object> getValueMap() {
		return this.valueMap;
	}

	/**
	 * 创建SQL构造器
	 *
	 * @param queryRule
	 */
	public QueryRuleSqlBuilder(QueryRule queryRule) {
		this.CURR_INDEX = 0;
		this.properties = new ArrayList<String>();
		this.values = new ArrayList<Object>();
		this.orders = new ArrayList<Order>();
		for (Rule rule : queryRule.getRuleList()) {
			switch (rule.getType()) {
				case QueryRule.BETWEEN:
					this.processBetween(rule);
					break;
				case QueryRule.EQ:
					this.processEqual(rule);
					break;
				case QueryRule.LIKE:
					this.processLike(rule);
					break;
				case QueryRule.NOTEQ:
					this.processNotEqual(rule);
					break;
				case QueryRule.GT:
					this.processGreaterThen(rule);
					break;
				case QueryRule.GE:
					this.processGreaterEqual(rule);
					break;
				case QueryRule.LT:
					this.processLessThen(rule);
					break;
				case QueryRule.LE:
					this.processLessEqual(rule);
					break;
				case QueryRule.IN:
					this.processIN(rule);
					break;
				case QueryRule.NOTIN:
					this.processNotIN(rule);
					break;
				case QueryRule.ISNULL:
					this.processIsNull(rule);
					break;
				case QueryRule.ISNOTNULL:
					this.processIsNotNull(rule);
					break;
				case QueryRule.ISEMPTY:
					this.processIsEmpty(rule);
					break;
				case QueryRule.ISNOTEMPTY:
					this.processIsNotEmpty(rule);
					break;
				case QueryRule.ASC_ORDER:
					this.processOrder(rule);
					break;
				case QueryRule.DESC_ORDER:
					this.processOrder(rule);
					break;
				default:
					throw new IllegalArgumentException("type " + rule.getType() + " not supported.");
			}
		}
		//拼装where语句
		this.appendWhereSql();
		//拼装排序语句
		this.appendOrderSql();
		//拼装参数值
		this.appendValues();
	}

	/**
	 * 去掉order
	 *
	 * @param sql
	 * @return
	 */
	protected String removeOrders(String sql) {
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(sql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}

	/**
	 * 去掉select
	 *
	 * @param sql
	 * @return
	 */
	protected String removeSelect(String sql) {
		if (sql.toLowerCase().matches("from\\s+")) {
			int beginPos = sql.toLowerCase().indexOf("from");
			return sql.substring(beginPos);
		}
		else {
			return sql;
		}
	}

	/**
	 * 处理like
	 *
	 * @param rule
	 */
	private void processLike(QueryRule.Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		Object obj = rule.getValues()[0];

		if (obj != null) {
			String value = obj.toString();
			if (!StringUtils.isEmpty(value)) {
				value = value.replace('*', '%');
				obj = value;
			}
		}
		this.add(rule.getAndOr(), rule.getPropertyName(), "like", "%" + rule.getValues()[0] + "%");
	}

	/**
	 * 处理between
	 *
	 * @param rule
	 */
	private void processBetween(Rule rule) {
		if ((ArrayUtils.isEmpty(rule.getValues()))
				    || (rule.getValues().length < 2)) {
			return;
		}
		this.add(rule.getAndOr(), rule.getPropertyName(), "", "between", rule.getValues()[0], "and");
		this.add(0, "", "", "", rule.getValues()[1], "");
	}

	/**
	 * 处理 =
	 *
	 * @param rule
	 */
	private void processEqual(QueryRule.Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		this.add(rule.getAndOr(), rule.getPropertyName(), "=", rule.getValues()[0]);
	}

	/**
	 * 处理 <>
	 *
	 * @param rule
	 */
	private void processNotEqual(Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		this.add(rule.getAndOr(), rule.getPropertyName(), "<>", rule.getValues()[0]);
	}

	/**
	 * 处理 >
	 *
	 * @param rule
	 */
	private void processGreaterThen(
			Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		this.add(rule.getAndOr(), rule.getPropertyName(), ">", rule.getValues()[0]);
	}

	/**
	 * 处理>=
	 *
	 * @param rule
	 */
	private void processGreaterEqual(
			Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		this.add(rule.getAndOr(), rule.getPropertyName(), ">=", rule.getValues()[0]);
	}

	/**
	 * 处理<
	 *
	 * @param rule
	 */
	private void processLessThen(Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		this.add(rule.getAndOr(), rule.getPropertyName(), "<", rule.getValues()[0]);
	}

	/**
	 * 处理<=
	 *
	 * @param rule
	 */
	private void processLessEqual(
			Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		this.add(rule.getAndOr(), rule.getPropertyName(), "<=", rule.getValues()[0]);
	}

	/**
	 * 处理  is null
	 *
	 * @param rule
	 */
	private void processIsNull(Rule rule) {
		this.add(rule.getAndOr(), rule.getPropertyName(), "is null", null);
	}

	/**
	 * 处理 is not null
	 *
	 * @param rule
	 */
	private void processIsNotNull(Rule rule) {
		this.add(rule.getAndOr(), rule.getPropertyName(), "is not null", null);
	}

	/**
	 * 处理  <>''
	 *
	 * @param rule
	 */
	private void processIsNotEmpty(Rule rule) {
		this.add(rule.getAndOr(), rule.getPropertyName(), "<>", "''");
	}

	/**
	 * 处理 =''
	 *
	 * @param rule
	 */
	private void processIsEmpty(Rule rule) {
		this.add(rule.getAndOr(), rule.getPropertyName(), "=", "''");
	}


	/**
	 * 处理in和not in
	 *
	 * @param rule
	 * @param name
	 */
	private void inAndNotIn(Rule rule, String name) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		if ((rule.getValues().length == 1) && (rule.getValues()[0] != null)
				    && (rule.getValues()[0] instanceof List)) {
			List<Object> list = (List) rule.getValues()[0];

			if ((list != null) && (list.size() > 0)) {
				for (int i = 0; i < list.size(); i++) {
					if (i == 0 && i == list.size() - 1) {
						this.add(rule.getAndOr(), rule.getPropertyName(), "", name + " (", list.get(i), ")");
					}
					else if (i == 0 && i < list.size() - 1) {
						this.add(rule.getAndOr(), rule.getPropertyName(), "", name + " (", list.get(i), "");
					}
					if (i > 0 && i < list.size() - 1) {
						this.add(0, "", ",", "", list.get(i), "");
					}
					if (i == list.size() - 1 && i != 0) {
						this.add(0, "", ",", "", list.get(i), ")");
					}
				}
			}
		}
		else {
			Object[] list = rule.getValues();
			for (int i = 0; i < list.length; i++) {
				if (i == 0 && i == list.length - 1) {
					this.add(rule.getAndOr(), rule.getPropertyName(), "", name + " (", list[i], ")");
				}
				else if (i == 0 && i < list.length - 1) {
					this.add(rule.getAndOr(), rule.getPropertyName(), "", name + " (", list[i], "");
				}
				if (i > 0 && i < list.length - 1) {
					this.add(0, "", ",", "", list[i], "");
				}
				if (i == list.length - 1 && i != 0) {
					this.add(0, "", ",", "", list[i], ")");
				}
			}
		}
	}

	/**
	 * 处理 not in
	 *
	 * @param rule
	 */
	private void processNotIN(Rule rule) {
		this.inAndNotIn(rule, "not in");
	}

	/**
	 * 处理 in
	 *
	 * @param rule
	 */
	private void processIN(Rule rule) {
		this.inAndNotIn(rule, "in");
	}

	/**
	 * 处理 order by
	 *
	 * @param rule 查询规则
	 */
	private void processOrder(Rule rule) {
		switch (rule.getType()) {
			case QueryRule.ASC_ORDER:
				// propertyName非空
				if (!StringUtils.isEmpty(rule.getPropertyName())) {
					this.orders.add(Order.asc(rule.getPropertyName()));
				}
				break;
			case QueryRule.DESC_ORDER:
				// propertyName非空
				if (!StringUtils.isEmpty(rule.getPropertyName())) {
					this.orders.add(Order.desc(rule.getPropertyName()));
				}
				break;
			default:
				break;
		}
	}


	/**
	 * 加入到sql查询规则队列
	 *
	 * @param andOr and 或者 or
	 * @param key   列名
	 * @param split 列名与值之间的间隔
	 * @param value 值
	 */
	private void add(int andOr, String key, String split, Object value) {
		this.add(andOr, key, split, "", value, "");
	}

	/**
	 * 加入到sql查询规则队列
	 *
	 * @param andOr  and 或则 or
	 * @param key    列名
	 * @param split  列名与值之间的间隔
	 * @param prefix 值前缀
	 * @param value  值
	 * @param suffix 值后缀
	 */
	private void add(int andOr, String key, String split, String prefix, Object value, String suffix) {
		String andOrStr = (0 == andOr ? "" : (QueryRule.AND == andOr ? " and " : " or "));
		this.properties.add(this.CURR_INDEX, andOrStr + key + " " + split + prefix + (null != value ? " ? " : " ") + suffix);
		if (null != value) {
			this.values.add(this.CURR_INDEX, value);
			this.CURR_INDEX++;
		}
	}


	/**
	 * 拼装 where 语句
	 */
	private void appendWhereSql() {
		StringBuffer whereSql = new StringBuffer();
		for (String p : this.properties) {
			whereSql.append(p);
		}
		this.whereSql = this.removeSelect(this.removeOrders(whereSql.toString()));
	}

	/**
	 * 拼装排序语句
	 */
	private void appendOrderSql() {
		StringBuffer orderSql = new StringBuffer();
		for (int i = 0; i < this.orders.size(); i++) {
			if (i > 0 && i < this.orders.size()) {
				orderSql.append(",");
			}
			orderSql.append(this.orders.get(i).toString());
		}
		this.orderSql = this.removeSelect(this.removeOrders(orderSql.toString()));
	}

	/**
	 * 拼装参数值
	 */
	private void appendValues() {
		Object[] val = new Object[this.values.size()];
		for (int i = 0; i < this.values.size(); i++) {
			val[i] = this.values.get(i);
			this.valueMap.put(i, this.values.get(i));
		}
		this.valueArr = val;
	}

}
